// 类和动态内存分配
// 在49StringBad.cpp中有这样一个语句:
// int StringBad::num_strings = 0;
// 他会配合 static int num_strings; 声明,将静态成员num_strings初始化为0;
// 注意: 不能在类的声明中初始化静态成员变量,因为类的声明不分配内存

// vegnews.cpp -- using new and delete with classes
// compile with strngbad.cpp
#include <cstring>
#include <iostream>
using std::cout;
#include "49StringBad.hpp"

void callme1(StringBad &); // pass by reference
void callme2(StringBad);   // pass by value

int main()
{
    using std::endl;
    {
        cout << "Starting an inner block.\n";
        StringBad headline1("Celery Stalks at Midnight");
        StringBad headline2("Lettuce Prey");
        StringBad sports("Spinach Leaves Bowl for Dollars");
        cout << "headline1: " << headline1 << endl;
        cout << "headline2: " << headline2 << endl;
        cout << "sports: " << sports << endl;
        callme1(headline1);
        cout << "headline1: " << headline1 << endl;
        callme2(headline2);
        //  在调用值传递的函数的时候,StringBad通过值传递复制到了函数中的形参,然后程序调用了析构函数!!
        cout << "headline2: " << headline2 << endl;
        cout << "Initialize one object to another:\n";
        StringBad sailor = sports;
        cout << "sailor: " << sailor << endl;
        cout << "Assign one object to another:\n";
        StringBad knot;
        knot = headline1;       // 这里调用operator=(),删除了一个对象,这里也是数据复制中出现的问题 
        cout << "knot: " << knot << endl;
        cout << "Exiting the block.\n";
    }
    cout << "End of main()\n";
    // std::cin.get();
    return 0;
}

void callme1(StringBad &rsb)
{
    cout << "String passed by reference:\n";
    cout << "    \"" << rsb << "\"\n";
}

void callme2(StringBad sb)
{
    cout << "String passed by value:\n";
    cout << "    \"" << sb << "\"\n";
}

/*
    Starting an inner block.
    1: "Celery Stalks at Midnight" object created
    2: "Lettuce Prey" object created
    3: "Spinach Leaves Bowl for Dollars" object created
    headline1: Celery Stalks at Midnight
    headline2: Lettuce Prey
    sports: Spinach Leaves Bowl for Dollars
    String passed by reference:
        "Celery Stalks at Midnight"
    headline1: Celery Stalks at Midnight
    String passed by value:
        "Lettuce Prey"
    "Lettuce Prey" object deleted, 2 left
    headline2: p
    Initialize one object to another:
    sailor: Spinach Leaves Bowl for Dollars
    Assign one object to another:
    3: "C++" default object created
    knot: Celery Stalks at Midnight
    Exiting the block.
    "Celery Stalks at Midnight" object deleted, 2 left
    "Spinach Leaves Bowl for Dollars" object deleted, 1 left
    "?`
*/

// 特殊成员函数
// StringBad类中的为题是这样的,在类中,C++会有自动的函数生成
/*
    - 默认构造函数:  如果没有定义构造函数的话
    - 默认析构函数:  如果没有定义....
    - 复制构造函数:  如果没有定义....
    - 赋值运算符:    ..........
    - 地址运算符:    ..........

C++11新增成员函数: 额外提供了两个成员函数:
    - 移动构造函数:    ..........
    - 移动赋值运算符:    ..........
    // 有关于C++11的新增函数,后续再说
*/

// - 默认构造函数:  如果没有写,则创建一个没有被初始化的对象[代码块中什么也没有]
// Klunk::Klunk(){  };
// 在调用Klunk lunk;时创建调用函数创建对象,初始化后的值是未知的

// - 默认析构函数:  如果没有定义....

// - 复制构造函数:  如果没有定义....
// 通常复制构造函数的定义如下Class_name (const Class_name &);

// 何时调用复制构造函数: 下面给出几个调用实例,他们都将会调用复制构造函数:
// StringBad ditto(motto);
// StringBad metoo = motto;
// StringBad also = StringBad(motto);
// StringBad * pStringBad = new StringBad(motto);
// 每当程序生成了对象副本的时候,编译器都将会使用复制构造函数
// 上面通过值传递的函数参数同样也会调用复制构造函数创建一个临时对象

// 默认复制构造函数逐个复制非静态成员(成员复制也叫做浅复制),复制的是成员的值
// 像上面的程序中没有提供默认的复制构造函数,那么C++将会帮助我们生成一个默认的复制构造函数,而且复制里面的内容.
// 复制的内容除了static的那个计数的之外,还有指向那个包含内容的char *指针,当析构函数被调用的时候,这个指针就会指向一个已经被delete[]后的位置
// 这时就会引起错误!!
// 解决方案:
// 1.定义一个显示的复制构造函数,将内容进行深度复制,[个人观点:产生性能损耗,因为每次使用值传递就会复制一个新对象,删除一个对象]
StringBad::StringBad(const StringBad &st)
{
    num_strings++;
    len = st.len;
    str = new char[len + 1];
    std::strcpy(str, st.str);
    cout << num_strings << ": \"" << str << "\" object created\n";
}
// 必须定义一个复制构造函数的原因在于: 一些类成员使用new初始化,指向数据的指针,而不是数据本身


// 
// - 赋值运算符:    ..........
// - 地址运算符:    ..........

// PS D:\coding\learnC++\Learning\lesson> g++ -Wall -g .\49ClassAndDynamicMemoryAlloc.cpp .\49StringBad.cpp .\49StringBad.hpp -o .\49ClassAndDynamicMemoryAlloc.exe
// PS D:\coding\learnC++\Learning\lesson> .\49ClassAndDynamicMemoryAlloc.exe
// Starting an inner block.
// 1: "Celery Stalks at Midnight" object created
// 2: "Lettuce Prey" object created
// 3: "Spinach Leaves Bowl for Dollars" object created
// headline1: Celery Stalks at Midnight
// headline2: Lettuce Prey
// sports: Spinach Leaves Bowl for Dollars
// String passed by reference:
//     "Celery Stalks at Midnight"
// headline1: Celery Stalks at Midnight
// 4: "Lettuce Prey" object created
// String passed by value:
//     "Lettuce Prey"
// "Lettuce Prey" object deleted, 3 left
// headline2: Lettuce Prey
// Initialize one object to another:
// 4: "Spinach Leaves Bowl for Dollars" object created
// sailor: Spinach Leaves Bowl for Dollars
// Assign one object to another:
// 5: "C++" default object created
// knot: Celery Stalks at Midnight
// Exiting the block.
// "Celery Stalks at Midnight" object deleted, 4 left
// "Spinach Leaves Bowl for Dollars" object deleted, 3 left
// "Spinach Leaves Bowl for Dollars" object deleted, 2 left
// "Lettuce Prey" object deleted, 1 left
// "po?

// 问题现在变成了这样
//      StringBad的其他问题 赋值运算符!
// ANSI C允许对结构赋值,C++允许对对象赋值,这是通过自动为类重载的复制运算符实现的.运算符原型如下:
// Class_name & Class_name::operator=(const Class_name &);
// StringBad类的运算符原型如下:
// StringBad & StringBad::operator=(const StringBad &);
// 将已有的对象赋值给另外一个对象的时候将会调用对象赋值运算符函数
// StringBad headline;
// StringBad knot;
// knot = headline;        // 这里将会调用对象赋值运算符函数

// 初始化的时候不一定使用赋值运算符
// StringBad metoo = knot;     // 使用复制构造函数,可能使用赋值运算符

// 如果这里有下面的语句
// StringBad sb;
// StringBad sb1 = StringBad("aquawius");
// StringBad sb2 = StringBad("AQUAWIUS");
// sb = sb1 = sb2;
// 等同于: sb.operator=(sb1.operator=(sb2));
// 可以想到重复释放一个内存 delete[];
StringBad & StringBad::operator=(const StringBad & st){
    // 如果比较的是同一块内存
    if (this == &st)
    {
        return *this;
    }

    // 不同内存,则释放内存,将内存重新分配,并赋值
    delete[] str;   // 如果没有释放,将会失去唯一的指针,这个内存将会失去控制,造成内存溢出
    len = st.len;
    str = new char[len + 1];
    std::strcpy(str,st.str);
    return *this;
}